using System;
using System.Collections.Generic;
using System.Linq;
using Cysharp.Threading.Tasks;
using UnityEngine.AddressableAssets;
using UnityEngine.ResourceManagement.AsyncOperations;
using UnityEngine.ResourceManagement.ResourceLocations;
using Object = UnityEngine.Object;

namespace AddressablesTool
{
    /// <summary>
    /// Addressables 资源管理器核心类
    /// 负责统一处理资源的异步加载、缓存管理、类型筛选、释放回收等全生命周期操作
    /// 解决单Key多资源（如Sprite与依赖Texture2D）的管理问题，提供类型安全的资源获取接口
    /// </summary>
    public class AssetLoader
    {
        /// <summary>
        /// 资源数据包装类
        /// 用于关联资源的唯一标识（Key）、加载句柄（Handle）、标签（Label）及类型信息
        /// </summary>
        private class AssetData
        {
            /// <summary>
            /// 资源的唯一标识（通常为Addressables地址或PrimaryKey）
            /// </summary>
            public string Key { get; }

            /// <summary>
            /// 资源加载句柄
            /// 用于跟踪加载状态、获取资源实例、释放资源的核心对象
            /// </summary>
            public AsyncOperationHandle Handle { get; }

            /// <summary>
            /// 资源标签
            /// 用于按标签管理和查找资源
            /// </summary>
            public string Label { get; }

            /// <summary>
            /// 构造函数：初始化资源数据包装
            /// </summary>
            /// <param name="key">资源唯一标识</param>
            /// <param name="handle">资源加载句柄</param>
            /// <param name="label">资源标签（可为null）</param>
            public AssetData(string key, AsyncOperationHandle handle, string label = null)
            {
                Key = key;
                Handle = handle;
                Label = label;
            }

            /// <summary>
            /// 获取当前资源的实际类型（如Sprite、Texture2D、GameObject等）
            /// </summary>
            public Type AssetType => Handle.Result.GetType();

            /// <summary>
            /// 泛型方法：获取指定类型的资源实例
            /// </summary>
            /// <typeparam name="T">目标资源类型</typeparam>
            /// <returns>类型匹配的资源实例</returns>
            public T Asset<T>() where T : Object
            {
                return Handle.Result as T;
            }

            /// <summary>
            /// 非泛型方法：获取基础Object类型的资源实例
            /// </summary>
            /// <returns>基础Object类型的资源实例</returns>
            public Object Asset() => Handle.Result as Object;
        }

        /// <summary>
        /// 已加载资源的缓存列表
        /// 支持单Key多资源（如同一Key下的Sprite和Texture2D）的存储与管理
        /// </summary>
        private List<AssetData> _loadedAssets = new List<AssetData>();

        /// <summary>
        /// 资源管理器初始化状态标记
        /// </summary>
        private bool _isInitialized;

        /// <summary>
        /// 初始化资源管理器
        /// </summary>
        public void Initialize()
        {
            if (_isInitialized)
                return;
            _isInitialized = true;
        }

        /// <summary>
        /// 异步加载指定地址的单个资源（泛型版）
        /// </summary>
        /// <typeparam name="T">目标资源类型</typeparam>
        /// <param name="address">资源地址</param>
        /// <param name="label">资源标签（可选）</param>
        /// <returns>加载成功的资源实例</returns>
        public async UniTask<T> LoadAssetAsync<T>(string address) where T : UnityEngine.Object
        {
            if (!_isInitialized)
            {
                throw new Exception($"AssetManager 未初始化，请先调用 Initialize() 方法");
            }

            // 先检查缓存，若资源已加载则直接返回
            T cachedAsset = GetLoadedAsset<T>(address);
            if (cachedAsset != null)
            {
                return cachedAsset;
            }

            AsyncOperationHandle<T> handle = Addressables.LoadAssetAsync<T>(address);
            var loadResult = await handle.ToUniTask();

            if (handle.Status != AsyncOperationStatus.Succeeded)
            {
                throw new Exception($"资源加载失败，地址：{address}，状态：{handle.Status}");
            }

            SaveAsset(address, handle);
            return loadResult;
        }

        /// <summary>
        /// 异步加载指定标签的所有资源
        /// </summary>
        /// <param name="label">资源标签</param>
        public async UniTask LoadAssetsByLabelAsync(string label)
        {
            if (!_isInitialized)
            {
                throw new Exception($"AssetManager 未初始化，请先调用 Initialize() 方法");
            }

            IList<IResourceLocation> resourceLocations = await Addressables.LoadResourceLocationsAsync(label).ToUniTask();

            await UniTask.WhenAll(resourceLocations.Select(location => SaveAssetAsync(location, label)).ToArray());
        }

        /// <summary>
        /// 私有辅助方法：异步加载单个资源位置并存入缓存
        /// </summary>
        /// <param name="location">资源位置</param>
        /// <param name="label">资源标签</param>
        private async UniTask SaveAssetAsync(IResourceLocation location, string label)
        {
            AsyncOperationHandle<Object> handle = Addressables.LoadAssetAsync<UnityEngine.Object>(location);
            await handle.ToUniTask();

            if (handle.Status != AsyncOperationStatus.Succeeded)
            {
                throw new Exception($"资源加载失败，PrimaryKey：{location.PrimaryKey}，状态：{handle.Status}");
            }

            SaveAsset(location.PrimaryKey, handle, label);
        }

        /// <summary>
        /// 私有辅助方法：将加载完成的资源存入缓存
        /// </summary>
        /// <param name="key">资源唯一标识</param>
        /// <param name="handle">资源加载句柄</param>
        /// <param name="label">资源标签（可选）</param>
        private void SaveAsset(string key, AsyncOperationHandle handle, string label = null)
        {
            if (handle.Status != AsyncOperationStatus.Succeeded)
            {
                throw new Exception($"资源未加载成功，无法存入缓存，Key：{key}");
            }

            AssetData assetData = new AssetData(key, handle, label);
            _loadedAssets.Add(assetData);
        }

        /// <summary>
        /// 根据Key和类型获取已加载的资源
        /// </summary>
        /// <typeparam name="T">目标资源类型</typeparam>
        /// <param name="key">资源唯一标识</param>
        /// <returns>Key和类型均匹配的资源实例</returns>
        public T GetLoadedAsset<T>(string key) where T : UnityEngine.Object
        {
            AssetData matchedAsset = _loadedAssets.Find(asset =>
                asset.Key.Equals(key, StringComparison.Ordinal) && asset.Asset() is T);

            if (matchedAsset == null)
            {
                throw new Exception($"资源未加载或类型不匹配，Key：{key}，目标类型：{typeof(T).Name}");
            }

            return matchedAsset.Asset() as T;
        }

        /// <summary>
        /// 根据标签获取所有已加载的资源
        /// </summary>
        /// <param name="label">资源标签</param>
        /// <returns>所有匹配标签的资源实例列表</returns>
        public List<Object> GetAssetsByLabel(string label)
        {
            if (string.IsNullOrEmpty(label))
            {
                throw new ArgumentNullException(nameof(label), "标签不能为空");
            }

            return _loadedAssets
                .Where(asset => string.Equals(asset.Label, label, StringComparison.OrdinalIgnoreCase))
                .Select(asset => asset.Asset())
                .ToList();
        }

        /// <summary>
        /// 根据标签和类型获取已加载的资源
        /// </summary>
        /// <typeparam name="T">目标资源类型</typeparam>
        /// <param name="label">资源标签</param>
        /// <returns>所有匹配标签和类型的资源实例列表</returns>
        public List<T> GetAssetsByLabel<T>(string label) where T : UnityEngine.Object
        {
            if (string.IsNullOrEmpty(label))
            {
                throw new ArgumentNullException(nameof(label), "标签不能为空");
            }

            return _loadedAssets
                .Where(asset => string.Equals(asset.Label, label, StringComparison.OrdinalIgnoreCase) && asset.Asset() is T)
                .Select(asset => asset.Asset<T>())
                .ToList();
        }

        /// <summary>
        /// 获取已加载资源中指定类型的第一个实例
        /// </summary>
        /// <typeparam name="T">目标资源类型</typeparam>
        /// <returns>第一个匹配类型的资源实例</returns>
        public T FindFirstAsset<T>() where T : UnityEngine.Object
        {
            AssetData firstMatchedAsset = _loadedAssets.FirstOrDefault(asset => asset.Asset() is T);

            if (firstMatchedAsset == null)
            {
                throw new Exception($"未加载指定类型的资源，目标类型：{typeof(T).Name}");
            }

            return firstMatchedAsset.Asset() as T;
        }

        /// <summary>
        /// 释放指定Key的所有已加载资源
        /// </summary>
        /// <param name="address">资源唯一标识</param>
        /// <param name="onAssetReleased">资源释放完成后的回调</param>
        public void ReleaseAsset(string address, Action<string> onAssetReleased = null)
        {
            var assetsToRelease = _loadedAssets.Where(asset =>
                asset.Key.Equals(address, StringComparison.Ordinal)).ToList();

            foreach (var assetToRelease in assetsToRelease)
            {
                if (assetToRelease.Handle.IsValid())
                {
                    Addressables.Release(assetToRelease.Handle);
                    _loadedAssets.Remove(assetToRelease);
                }
            }

            onAssetReleased?.Invoke(address);
        }

        /// <summary>
        /// 释放所有已加载的资源
        /// </summary>
        public void ReleaseAllAssets()
        {
            foreach (AssetData loadedAsset in _loadedAssets)
            {
                if (loadedAsset.Handle.IsValid())
                {
                    Addressables.Release(loadedAsset.Handle);
                }
            }

            _loadedAssets.Clear();
        }

        /// <summary>
        /// 释放指定标签的所有资源
        /// </summary>
        /// <param name="label">资源标签</param>
        public void ReleaseAssetsByLabel(string label)
        {
            if (string.IsNullOrEmpty(label))
            {
                throw new ArgumentNullException(nameof(label), "标签不能为空");
            }

            var assetsToRelease = _loadedAssets
                .Where(asset => string.Equals(asset.Label, label, StringComparison.OrdinalIgnoreCase))
                .ToList();

            foreach (var assetToRelease in assetsToRelease)
            {
                if (assetToRelease.Handle.IsValid())
                {
                    Addressables.Release(assetToRelease.Handle);
                    _loadedAssets.Remove(assetToRelease);
                }
            }
        }

        /// <summary>
        /// 检查指定Key的资源是否已加载完成
        /// </summary>
        /// <param name="address">资源唯一标识</param>
        /// <returns>true：资源已加载且有效；false：资源未加载、加载中或已失效</returns>
        public bool IsAssetLoaded(string address)
        {
            AssetData matchedAsset = _loadedAssets.FirstOrDefault(asset =>
                asset.Key.Equals(address, StringComparison.Ordinal));

            if (matchedAsset == null)
            {
                return false;
            }

            return matchedAsset.Handle.IsValid() && matchedAsset.Handle.IsDone;
        }

        /// <summary>
        /// 获取当前已加载资源的总数量
        /// </summary>
        /// <returns>已加载资源的总个数</returns>
        public int GetLoadedAssetsCount()
        {
            return _loadedAssets.Count;
        }

        /// <summary>
        /// 获取所有已加载资源的Key列表
        /// </summary>
        /// <returns>已加载资源的Key列表</returns>
        public List<string> GetLoadedAssetAddresses()
        {
            return _loadedAssets.Select(assetData => assetData.Key).ToList();
        }

        /// <summary>
        /// 获取所有已加载资源的标签列表（去重）
        /// </summary>
        /// <returns>已加载资源的标签列表</returns>
        public List<string> GetLoadedAssetLabels()
        {
            return _loadedAssets
                .Where(asset => !string.IsNullOrEmpty(asset.Label))
                .Select(asset => asset.Label)
                .Distinct()
                .ToList();
        }
    }
}